/**
 *
 * 判断是不是纯正的 Object
 * @param {*} value
 */
const isObject = (value: any) => Object.prototype.toString.call(value).slice(8, -1) === 'Object';

/**
 * 查找树结构数据节点 找到第一个满足的就返回 没找到返回 undefined
 *
 *(包含该节点的children)
 *
 * @param {object[]} tree 树结构数据
 * @param {function} predicate 遍历每一项执行的函数，参数是当前遍历到的节点数据，如果返回 Truthy ，将返回该节点
 * @param {string} [childrenField='children'] 子级字段名
 * @returns {object|undefined}
 * @example
 * const menus = [{ "id": "1", "name": "首页", "code": "trade", "pid": null }, { "id": "2", "name": "交易管理", "code": "trade", "pid": null, "children": [{ "id": "3", "name": "交易查询", "code": "trade-1", "pid": "2", "children": [{ "id": "4", "name": "交易查询-查询操作", "code": "trade-1-1", "pid": "3" }] }] }, { "id": "5", "name": "权限管理", "code": "authorization", "pid": null, "children": [{ "id": "6", "name": "角色管理", "code": "authorization-1", "pid": "5" }, { "id": "7", "name": "用户管理", "code": "authorization-2", "pid": "5" }] }];
 *
 * findTreeNode(menus, item=>item.id === '2');
 * // {"id":"2","name":"交易管理","code":"trade","pid":null,"children":[{"id":"3","name":"交易查询","code":"trade-1","pid":"2","children":[{"id":"4","name":"交易查询-查询操作","code":"trade-1-1","pid":"3"}]}]}
 *
 * findTreeNode(menus, item=>item.id === '7');
 * // {"id":"7","name":"用户管理","code":"authorization-2","pid":"5"}
 *
 * findTreeNode(menus, item=>item.id === 'not found');
 * // undefined
 */
function findTreeNode<T extends any, F extends (item: T) => boolean>(
  tree: T[],
  predicate: F,
  childrenField = 'children'
) {
  const stack: T[] = [];

  let node: T | undefined;

  if (!Array.isArray(tree)) {
    return node;
  }

  for (const item of tree) {
    stack.push(item);

    while (stack.length) {
      const temp = stack.pop() as T;
      if (predicate(temp)) {
        node = temp;
        break;
      }

      if (isObject(temp)) {
        // @ts-ignore
        const childs = temp[childrenField] as T[];
        if (Array.isArray(childs) && childs.length > 0) {
          childs.forEach((c) => {
            stack.push(c);
          });
        }
      }
    }

    if (node) {
      break;
    }
  }

  return node;
}

export default findTreeNode;

const menus = [
  { id: '1', name: '首页', code: 'trade', pid: null },
  {
    id: '2',
    name: '交易管理',
    code: 'trade',
    pid: null,
    children: [
      {
        id: '3',
        name: '交易查询',
        code: 'trade-1',
        pid: '2',
        children: [{ id: '4', name: '交易查询-查询操作', code: 'trade-1-1', pid: '3' }],
      },
    ],
  },
  {
    id: '5',
    name: '权限管理',
    code: 'authorization',
    pid: null,
    children: [
      { id: '6', name: '角色管理', code: 'authorization-1', pid: '5' },
      { id: '7', name: '用户管理', code: 'authorization-2', pid: '5' },
    ],
  },
];

const newCurrentNode = findTreeNode(menus, (item) => item.id === '2');
const curNode = {
  id: '2',
  name: '交易管理',
  code: 'trade',
  pid: null,
  children: [
    {
      id: '3',
      name: '交易查询',
      code: 'trade-1',
      pid: '2',
      children: [{ id: '4', name: '交易查询-查询操作', code: 'trade-1-1', pid: '3' }],
    },
  ],
};
